home *** CD-ROM | disk | FTP | other *** search
/ Enter 2006 September / Enter 09 2006.iso / Internet / SpamExperts Home 1.1 / SpamExperts Home.exe / lib / spamexperts.modules / dns / renderer.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-07-14  |  9.8 KB  |  286 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. '''Help for building DNS wire format messages'''
  5. import cStringIO
  6. import struct
  7. import random
  8. import time
  9. import dns.exception as dns
  10. import dns.tsig as dns
  11. QUESTION = 0
  12. ANSWER = 1
  13. AUTHORITY = 2
  14. ADDITIONAL = 3
  15.  
  16. class Renderer(object):
  17.     """Helper class for building DNS wire-format messages.
  18.  
  19.     Most applications can use the higher-level L{dns.message.Message}
  20.     class and its to_wire() method to generate wire-format messages.
  21.     This class is for those applications which need finer control
  22.     over the generation of messages.
  23.     
  24.     Typical use::
  25.  
  26.         r = dns.renderer.Renderer(id=1, flags=0x80, max_size=512)
  27.         r.add_question(qname, qtype, qclass)
  28.         r.add_rrset(dns.renderer.ANSWER, rrset_1)
  29.         r.add_rrset(dns.renderer.ANSWER, rrset_2)
  30.         r.add_rrset(dns.renderer.AUTHORITY, ns_rrset)
  31.         r.add_edns(0, 0, 4096)
  32.         r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_1)
  33.         r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_2)
  34.         r.write_header()
  35.         r.add_tsig(keyname, secret, 300, 1, 0, '', request_mac)
  36.         wire = r.get_wire()
  37.  
  38.     @ivar output: where rendering is written
  39.     @type output: cStringIO.StringIO object
  40.     @ivar id: the message id
  41.     @type id: int
  42.     @ivar flags: the message flags
  43.     @type flags: int
  44.     @ivar max_size: the maximum size of the message
  45.     @type max_size: int
  46.     @ivar origin: the origin to use when rendering relative names
  47.     @type origin: dns.name.Name object
  48.     @ivar compress: the compression table
  49.     @type compress: dict
  50.     @ivar section: the section currently being rendered
  51.     @type section: int (dns.renderer.QUESTION, dns.renderer.ANSWER,
  52.     dns.renderer.AUTHORITY, or dns.renderer.ADDITIONAL)
  53.     @ivar counts: list of the number of RRs in each section
  54.     @type counts: int list of length 4
  55.     @ivar mac: the MAC of the rendered message (if TSIG was used)
  56.     @type mac: string
  57.     """
  58.     
  59.     def __init__(self, id = None, flags = 0, max_size = 65535, origin = None):
  60.         '''Initialize a new renderer.
  61.  
  62.         @param id: the message id
  63.         @type id: int
  64.         @param flags: the DNS message flags
  65.         @type flags: int
  66.         @param max_size: the maximum message size; the default is 65535.
  67.         If rendering results in a message greater than I{max_size},
  68.         then L{dns.exception.TooBig} will be raised.
  69.         @type max_size: int
  70.         @param origin: the origin to use when rendering relative names
  71.         @type origin: dns.name.Namem or None.
  72.         '''
  73.         self.output = cStringIO.StringIO()
  74.         if id is None:
  75.             self.id = random.randint(0, 65535)
  76.         else:
  77.             self.id = id
  78.         self.flags = flags
  79.         self.max_size = max_size
  80.         self.origin = origin
  81.         self.compress = { }
  82.         self.section = QUESTION
  83.         self.counts = [
  84.             0,
  85.             0,
  86.             0,
  87.             0]
  88.         self.output.write('\x00' * 12)
  89.         self.mac = ''
  90.  
  91.     
  92.     def _rollback(self, where):
  93.         '''Truncate the output buffer at offset I{where}, and remove any
  94.         compression table entries that pointed beyond the truncation
  95.         point.
  96.  
  97.         @param where: the offset
  98.         @type where: int
  99.         '''
  100.         self.output.seek(where)
  101.         self.output.truncate()
  102.         keys_to_delete = []
  103.         for k, v in self.compress.iteritems():
  104.             if v >= where:
  105.                 keys_to_delete.append(k)
  106.                 continue
  107.         
  108.         for k in keys_to_delete:
  109.             del self.compress[k]
  110.         
  111.  
  112.     
  113.     def _set_section(self, section):
  114.         """Set the renderer's current section.
  115.  
  116.         Sections must be rendered order: QUESTION, ANSWER, AUTHORITY,
  117.         ADDITIONAL.  Sections may be empty.
  118.  
  119.         @param section: the section
  120.         @type section: int
  121.         @raises dns.exception.FormError: an attempt was made to set
  122.         a section value less than the current section.
  123.         """
  124.         if self.section != section:
  125.             if self.section > section:
  126.                 raise dns.exception.FormError
  127.             
  128.             self.section = section
  129.         
  130.  
  131.     
  132.     def add_question(self, qname, rdtype, rdclass = dns.rdataclass.IN):
  133.         '''Add a question to the message.
  134.  
  135.         @param qname: the question name
  136.         @type qname: dns.name.Name
  137.         @param rdtype: the question rdata type
  138.         @type rdtype: int
  139.         @param rdclass: the question rdata class
  140.         @type rdclass: int
  141.         '''
  142.         self._set_section(QUESTION)
  143.         before = self.output.tell()
  144.         qname.to_wire(self.output, self.compress, self.origin)
  145.         self.output.write(struct.pack('!HH', rdtype, rdclass))
  146.         after = self.output.tell()
  147.         if after >= self.max_size:
  148.             self._rollback(before)
  149.             raise dns.exception.TooBig
  150.         
  151.         self.counts[QUESTION] += 1
  152.  
  153.     
  154.     def add_rrset(self, section, rrset, **kw):
  155.         """Add the rrset to the specified section.
  156.  
  157.         Any keyword arguments are passed on to the rdataset's to_wire()
  158.         routine.
  159.         
  160.         @param section: the section
  161.         @type section: int
  162.         @param rrset: the rrset
  163.         @type rrset: dns.rrset.RRset object
  164.         """
  165.         self._set_section(section)
  166.         before = self.output.tell()
  167.         n = rrset.to_wire(self.output, self.compress, self.origin, **kw)
  168.         after = self.output.tell()
  169.         if after >= self.max_size:
  170.             self._rollback(before)
  171.             raise dns.exception.TooBig
  172.         
  173.         self.counts[section] += n
  174.  
  175.     
  176.     def add_rdataset(self, section, name, rdataset, **kw):
  177.         """Add the rdataset to the specified section, using the specified
  178.         name as the owner name.
  179.  
  180.         Any keyword arguments are passed on to the rdataset's to_wire()
  181.         routine.
  182.  
  183.         @param section: the section
  184.         @type section: int
  185.         @param name: the owner name
  186.         @type name: dns.name.Name object
  187.         @param rdataset: the rdataset
  188.         @type rdataset: dns.rdataset.Rdataset object
  189.         """
  190.         self._set_section(section)
  191.         before = self.output.tell()
  192.         n = rdataset.to_wire(name, self.output, self.compress, self.origin, **kw)
  193.         after = self.output.tell()
  194.         if after >= self.max_size:
  195.             self._rollback(before)
  196.             raise dns.exception.TooBig
  197.         
  198.         self.counts[section] += n
  199.  
  200.     
  201.     def add_edns(self, edns, ednsflags, payload):
  202.         """Add an EDNS OPT record to the message.
  203.  
  204.         @param edns: The EDNS level to use.
  205.         @type edns: int
  206.         @param ednsflags: EDNS flag values.
  207.         @type ednsflags: int
  208.         @param payload: The EDNS sender's payload field, which is the maximum
  209.         size of UDP datagram the sender can handle.
  210.         @type payload: int
  211.         @see: RFC 2671
  212.         """
  213.         self._set_section(ADDITIONAL)
  214.         before = self.output.tell()
  215.         self.output.write(struct.pack('!BHHIH', 0, dns.rdatatype.OPT, payload, ednsflags, 0))
  216.         after = self.output.tell()
  217.         if after >= self.max_size:
  218.             self._rollback(before)
  219.             raise dns.exception.TooBig
  220.         
  221.         self.counts[ADDITIONAL] += 1
  222.  
  223.     
  224.     def add_tsig(self, keyname, secret, fudge, id, tsig_error, other_data, request_mac):
  225.         '''Add a TSIG signature to the message.
  226.         
  227.         @param keyname: the TSIG key name
  228.         @type keyname: dns.name.Name object
  229.         @param secret: the secret to use
  230.         @type secret: string
  231.         @param fudge: TSIG time fudge; default is 300 seconds.
  232.         @type fudge: int
  233.         @param id: the message id to encode in the tsig signature
  234.         @type id: int
  235.         @param tsig_error: TSIG error code; default is 0.
  236.         @type tsig_error: int
  237.         @param other_data: TSIG other data.
  238.         @type other_data: string
  239.         @param request_mac: This message is a response to the request which
  240.         had the specified MAC.
  241.         @type request_mac: string
  242.         '''
  243.         self._set_section(ADDITIONAL)
  244.         before = self.output.tell()
  245.         s = self.output.getvalue()
  246.         (tsig_rdata, self.mac, ctx) = dns.tsig.hmac_md5(s, keyname, secret, int(time.time()), fudge, id, tsig_error, other_data, request_mac)
  247.         keyname.to_wire(self.output, self.compress, self.origin)
  248.         self.output.write(struct.pack('!HHIH', dns.rdatatype.TSIG, dns.rdataclass.ANY, 0, 0))
  249.         rdata_start = self.output.tell()
  250.         self.output.write(tsig_rdata)
  251.         after = self.output.tell()
  252.         if not after - rdata_start < 65536:
  253.             raise AssertionError
  254.         if after >= self.max_size:
  255.             self._rollback(before)
  256.             raise dns.exception.TooBig
  257.         
  258.         self.output.seek(rdata_start - 2)
  259.         self.output.write(struct.pack('!H', after - rdata_start))
  260.         self.counts[ADDITIONAL] += 1
  261.         self.output.seek(10)
  262.         self.output.write(struct.pack('!H', self.counts[ADDITIONAL]))
  263.         self.output.seek(0, 2)
  264.  
  265.     
  266.     def write_header(self):
  267.         '''Write the DNS message header.
  268.  
  269.         Writing the DNS message header is done asfter all sections
  270.         have been rendered, but before the optional TSIG signature
  271.         is added.
  272.         '''
  273.         self.output.seek(0)
  274.         self.output.write(struct.pack('!HHHHHH', self.id, self.flags, self.counts[0], self.counts[1], self.counts[2], self.counts[3]))
  275.         self.output.seek(0, 2)
  276.  
  277.     
  278.     def get_wire(self):
  279.         '''Return the wire format message.
  280.  
  281.         @rtype: string
  282.         '''
  283.         return self.output.getvalue()
  284.  
  285.  
  286.